import * as THREE from 'three';
import Event from './Event';
import Debug from './tools/Debug';
import GreenHouse from './GreenHouse/GreenHouse';
import Field from './Field/Field';
import { createCameraTween } from './tools/useTween';
import { useBloom } from './tools/useBloom';

interface SpaceAssemblerEvents {
  [SpaceAssembler.EVENT_BUILDING_UPDATE]: (params: { greenhouses?: Editor.GreenHouse[]; fields?: Editor.Field[] }) => void;
}

/** Space模式位姿数据 */
interface Space_Pos {
  /** 基准点, 缺省 默认(0,0,0) */
  position?: THREE.Vector3Like | THREE.Vector3;
  /** 基准旋转，默认不旋转 */
  quaternion?: THREE.QuaternionLike | THREE.Quaternion;
}

/** 用于在Space中渲染大棚 */
export interface Space_GreenHouseRenderData extends Editor.GreenHouseRenderData {
  /** 位姿数据 */
  pos?: Space_Pos;
}

/** 用于在Space中渲染田块 */
export interface Space_FieldRenderData extends Editor.FieldRenderData {
  /** 位姿数据 */
  pos?: Space_Pos;
}
export interface Space_BuildingSaveData {
  /** GreenHouse 或 Field 的唯一id */
  id: number | string;
  pos?: Space_Pos;
}

interface SpaceAssemblerOption {
  scene: THREE.Scene;
  camera: THREE.PerspectiveCamera;
  renderer: THREE.WebGLRenderer;
  controls: Editor.Controls;
  /** 预加载大棚 */
  preloadGreenHouses?: Array<Space_GreenHouseRenderData>;
  /** 预加载田块 */
  preloadFields?: Array<Space_FieldRenderData>;
}

export default class SpaceAssembler extends Event<SpaceAssemblerEvents> {
  /** 大棚or田块 更新 */
  static EVENT_BUILDING_UPDATE = 'BUILDING_UPDATE';

  private readonly namespace = 'SpaceAssembler';

  private _greenhousesMap: Map<number | string, Editor.GreenHouse> = new Map();
  greenhousesMap: Map<number | string, Editor.GreenHouse>;

  private _fieldsMap: Map<number | string, Editor.Field> = new Map();
  fieldsMap: Map<number | string, Editor.Field>;

  scene: THREE.Scene;
  camera: THREE.PerspectiveCamera;
  controls: Editor.Controls;
  composer: ReturnType<typeof useBloom>['composer'];
  outlinePass: ReturnType<typeof useBloom>['outlinePass'];

  /** 正在编辑的building */
  private _editingBuilding: Editor.Building | null = null;

  constructor(option: SpaceAssemblerOption) {
    super();
    const { scene, controls, camera } = option;
    this.scene = scene;
    this.camera = camera;
    this.controls = controls;
    this._intGreenhousesMapProxy();
    this._initFieldMapProxy();
    this._init(option);
  }

  private _intGreenhousesMapProxy() {
    const self = this;
    this.greenhousesMap = new Proxy(this._greenhousesMap, {
      get(target, prop, receiver) {
        if (prop === 'set') {
          return (id: number | string, greenhouse: Editor.GreenHouse) => {
            const result = target.set(id, greenhouse);
            self._emitEvent(SpaceAssembler.EVENT_BUILDING_UPDATE, {
              greenhouses: Array.from(target.values()),
            });
            return result;
          };
        }
        if (prop === 'delete') {
          return function (id: number | string) {
            const result = target.delete(id);
            self._emitEvent(SpaceAssembler.EVENT_BUILDING_UPDATE, {
              greenhouses: Array.from(target.values()),
            });
            return result;
          };
        }
        const value = Reflect.get(target, prop, receiver);
        if (typeof value === 'function') {
          return value.bind(target);
        }
        return value;
      },
    });
  }

  private _initFieldMapProxy() {
    const self = this;
    this.fieldsMap = new Proxy(this._fieldsMap, {
      get(target, prop, receiver) {
        if (prop === 'set') {
          return (id: number | string, field: Editor.Field) => {
            const result = target.set(id, field);
            self._emitEvent(SpaceAssembler.EVENT_BUILDING_UPDATE, {
              fields: Array.from(target.values()),
            });
            return result;
          };
        }
        if (prop === 'delete') {
          return function (id: number | string) {
            const result = target.delete(id);
            self._emitEvent(SpaceAssembler.EVENT_BUILDING_UPDATE, {
              fields: Array.from(target.values()),
            });
            return result;
          };
        }
        const value = Reflect.get(target, prop, receiver);
        if (typeof value === 'function') {
          return value.bind(target);
        }
        return value;
      },
    });
  }

  private _init(option: SpaceAssemblerOption) {
    const { preloadGreenHouses, preloadFields, scene, camera, renderer } = option;
    // 预渲染大棚
    if (Array.isArray(preloadGreenHouses)) {
      preloadGreenHouses.forEach((renderData) => {
        this.addGreenHouse(renderData);
      });
    }
    // 预渲染田块
    if (Array.isArray(preloadFields)) {
      preloadFields.forEach((renderData) => {
        this.addField(renderData);
      });
    }
    // 启用轮廓bloom
    const { composer, outlinePass } = useBloom({
      scene,
      camera,
      renderer,
    });
    this.composer = composer;
    this.outlinePass = outlinePass;
  }

  /** 向当前space中添加一个新大棚
   * - 该操作建立起space和某个大棚的映射关系
   */
  addGreenHouse(renderData: Space_GreenHouseRenderData) {
    const { id, pos = {}, preloadCropPlotRawDatas = [], preloadDeviceRawDatas = [], preloadCropRackRawDatas = [], ..._resRenderData } = renderData;

    if (this.greenhousesMap.has(id)) return;
    const greenhouse = new GreenHouse({
      ..._resRenderData,
      // MARK:必须显式加入controls
      controls: this.controls,
      id,
      scene: this.scene,
      plugins: [
        {
          name: 'PlantAreaManager',
          option: {
            camera: this.camera,
            preloadCropPlotRawDatas,
            preloadCropRackRawDatas,
          },
        },
        {
          name: 'DeviceManager',
          option: {
            camera: this.camera,
            preloadDeviceRawDatas,
          },
        },
      ],
    });
    greenhouse.on(GreenHouse.EVENT_BASEMODEL_LOADED, (model: THREE.Object3D | null) => {
      if (model) {
        // 修正大棚位姿
        pos.position && model.position.copy(pos.position);
        pos.quaternion && model.quaternion.copy(pos.quaternion);

        // 添加到场景
        this.greenhousesMap.set(id, greenhouse);
      } else {
        Debug.error(this.namespace, '-init-模型加载失败');
      }
    });

    // 修正初始插件位姿
    greenhouse.on(GreenHouse.EVENT_BASEMODEL_PLUGINS_LOADED, (plugins: Editor.Plugins) => {
      Object.keys(plugins).forEach((name) => {
        if (name === 'PlantAreaManager') {
          const group = plugins[name].cropStaffGroup;
          pos.position && greenhouse.followBasePoint(group);
          pos.quaternion && greenhouse.followQuaternion(group);
        }
        if (name === 'DeviceManager') {
          const group = plugins[name].devicesGroup;
          pos.position && greenhouse.followBasePoint(group);
          pos.quaternion && greenhouse.followQuaternion(group);
        }
      });
    });
  }

  /** 根据id 删除大棚
   *  - 该操作解除space和某个大棚的映射关系
   */
  removeGreenHouseById(id: number | string) {
    // 取出greenhouse
    const greenhouse = this.greenhousesMap.get(id);
    if (!greenhouse) return;

    // 如果移除的是正在编辑的大棚，应该先安全取消编辑态
    if (this.controls.isObjectControlled(greenhouse.model)) {
      this.closeEdit();
    }
    // 删除操作
    greenhouse.destroy();
    this.greenhousesMap.delete(id);
  }

  /** 向当前space中添加一个新田块
   * - 该操作建立起space和某个田块的映射关系
   **/
  addField(renderData: Space_FieldRenderData) {
    const { id, pos = {}, preloadCropPlotRawDatas = [], preloadDeviceRawDatas = [] } = renderData;
    if (this.fieldsMap.has(id)) return;
    const field = new Field({
      ...renderData,
      // MARK:必须显式加入controls
      controls: this.controls,
      scene: this.scene,
      plugins: [
        {
          name: 'PlantAreaManager',
          option: {
            camera: this.camera,
            preloadCropPlotRawDatas,
          },
        },
        {
          name: 'DeviceManager',
          option: {
            camera: this.camera,
            preloadDeviceRawDatas,
          },
        },
      ],
    });
    field.on(Field.EVENT_BASEMODEL_LOADED, (model: THREE.Object3D | null) => {
      if (model) {
        // 修正田块位姿
        pos.position && model.position.copy(pos.position);
        pos.quaternion && model.quaternion.copy(pos.quaternion);
        // 添加到场景
        this.fieldsMap.set(id, field);
      }
    });
    // 修正初始插件位姿(TODO: 适配RoadManager)
    field.on(Field.EVENT_BASEMODEL_PLUGINS_LOADED, (plugins: Editor.Plugins) => {
      Object.keys(plugins).forEach((name) => {
        if (name === 'PlantAreaManager') {
          const group = plugins[name].cropStaffGroup;
          pos.position && field.followBasePoint(group);
          pos.quaternion && field.followQuaternion(group);
        }
        if (name === 'DeviceManager') {
          const group = plugins[name].devicesGroup;
          pos.position && field.followBasePoint(group);
          pos.quaternion && field.followQuaternion(group);
        }
      });
    });
  }

  /**
   * 根据id 删除田块
   *  - 该操作解除space和某个田块的映射关系
   */
  removeFieldById(id: number | string) {
    const field = this.fieldsMap.get(id);
    if (!field) return;

    // 如果移除的是正在编辑的田块，应该先安全取消编辑态
    if (this.controls.isObjectControlled(field.model)) {
      this.closeEdit();
    }

    // 删除操作
    field.destroy();
    this.fieldsMap.delete(id);
  }

  /**
   * 开启编辑模式
   * flow:
   *  - ✅明确要attach的object + attach
   * FIXME: 考虑是否允许 使用enableTransformModeMenu
   *  - 快速俯视图切换
   *  - ⭕开启快速键盘控制
   */
  openEdit(building: Editor.Building) {
    if (this._editingBuilding) this.closeEdit();
    const { transformControls, orbitControls } = this.controls;
    const { basePoint } = building.getInfo();
    createCameraTween({
      camera: this.camera,
      controls: orbitControls,
      endPos: basePoint.clone().addScalar(500),
      endTarget: basePoint.clone(),
      duration: 500,
    });

    // 开启跟随变换
    const deviceManager = building.getPluginByName('DeviceManager');
    deviceManager && deviceManager.enablefollowTransform();
    const plantAreaManager = building.getPluginByName('PlantAreaManager');
    plantAreaManager && plantAreaManager.enablefollowTransform();
    // 开启transformcontrol菜单
    this.controls.enableTransformModeMenu({
      initialMode: 'translate',
      visibleModes: [
        {
          mode: 'translate',
          callback: () => {
            transformControls.showX = true;
            transformControls.showY = false;
            transformControls.showZ = true;
          },
        },
        {
          mode: 'rotate',
          callback: () => {
            transformControls.rotationSnap = Math.PI / 4;
            transformControls.showX = false;
            transformControls.showY = true;
            transformControls.showZ = false;
          },
        },
      ],
    });
    transformControls.attach(building.model);

    this.outlinePass.selectedObjects = [building.model];
    this._editingBuilding = building;
  }

  closeEdit() {
    if (!this._editingBuilding) return;
    // 关闭跟随变换
    const deviceManager = this._editingBuilding.getPluginByName('DeviceManager');
    deviceManager && deviceManager.disablefollowTransform();
    const plantAreaManager = this._editingBuilding.getPluginByName('PlantAreaManager');
    plantAreaManager && plantAreaManager.disablefollowTransform();

    // 销毁enableTransformModeMenu
    this.controls.detachTransfromControls();
    this.outlinePass.selectedObjects = [];
    this._editingBuilding = null;
  }

  /** 在requestAnimation中调用 */
  update() {
    this.composer.render();
  }

  /**
   * TODO: 需要重构设计space保存接口
   *  获取当前space中的所有田块和大棚的接口保存数据 */
  getSaveData(): {
    greenhouses: Array<Space_BuildingSaveData>;
    fields: Array<Space_BuildingSaveData>;
  } {
    const greenhouses: Array<Space_BuildingSaveData> = [];
    const generatePosLikeData = (model: THREE.Object3D) => {
      return {
        position: {
          x: model.position.x,
          y: model.position.y,
          z: model.position.z,
        },
        quaternion: {
          x: model.quaternion.x,
          y: model.quaternion.y,
          z: model.quaternion.z,
          w: model.quaternion.w,
        },
      };
    };
    this.greenhousesMap.forEach((greenhouse, id) => {
      greenhouses.push({
        id,
        pos: generatePosLikeData(greenhouse.model),
      });
    });
    const fields: Array<Space_BuildingSaveData> = [];
    this.fieldsMap.forEach((field, id) => {
      fields.push({
        id,
        pos: generatePosLikeData(field.model),
      });
    });
    console.log('[SpaceAssembler] getSaveData', greenhouses, fields);
    return {
      greenhouses,
      fields,
    };
  }

  /**
   * TEST 测试销毁效果
   */
  destroy() {
    this.greenhousesMap.forEach((greenhouse) => greenhouse.destroy());
    this.fieldsMap.forEach((field) => field.destroy());
    this.greenhousesMap = null;
    this.fieldsMap = null;
    this.composer && this.composer.dispose();
    this.composer = null;
    this.outlinePass = null;
  }
}
